package com.yy.config;

import com.alibaba.fastjson2.JSON;
import com.yy.constant.AuthConstant;
import com.yy.constant.BusinessEnum;
import com.yy.constant.HttpConstant;
import com.yy.model.LoginResult;
import com.yy.model.Result;

import com.yy.service.impl.UserDetailsServiceImpl;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.AccountExpiredException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;


import java.io.PrintWriter;
import java.time.Duration;
import java.util.UUID;

/**
 * security的配置类
 * 作者 yy
 */
@Configuration
@Log4j2
public class AuthSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private StringRedisTemplate redisTemplate;


    @Autowired
    private UserDetailsServiceImpl userDetailsService;
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
          auth.userDetailsService(userDetailsService);
    }

    /**
     * 网络请求配置
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //关闭跨站请求伪造的拦截
        http.csrf().disable();
        http.cors().disable();
        //session STATELESS
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        //配置登入信息
        http.formLogin()
                .loginProcessingUrl(AuthConstant.LOGIN_URL)
                .successHandler(authenticationSuccessHandler())
                .failureHandler(authenticationFailureHandler());
        //退出的配置
        http.logout()
                .logoutSuccessHandler(logoutSuccessHandler())
                .logoutUrl(AuthConstant.LOGOUT_URL);

        //其他所有接口，都必须认证后才可以访问（必须在 securityContext 中有认证对象 才可以访问）
        http.authorizeHttpRequests()
                .anyRequest()
                .authenticated();
    }


    /**
     * 登入成功的处理器
     */
    @Bean
    public AuthenticationSuccessHandler authenticationSuccessHandler(){
        return (request, response, authentication) ->{
            response.setContentType(HttpConstant.APPLICATION_JSON);
            response.setCharacterEncoding(HttpConstant.UTF_8);
            String token= UUID.randomUUID().toString();
            // authentication.getPrincipal() 就是securityUser
            String userStr= JSON.toJSONString(authentication.getPrincipal());
            redisTemplate.opsForValue().set(AuthConstant.LOGIN_TOKEN_PREFIX+token,userStr, Duration.ofDays(AuthConstant.TOKEN_TIME));
            // token返回 {code:200,msg:ok,data:{accessToken:token,expireIn:7200}}
            LoginResult loginResult=new LoginResult(token,AuthConstant.TOKEN_TIME);
            Result result=Result.success(loginResult);
            String s=JSON.toJSONString(result);
            PrintWriter writer=response.getWriter();
            writer.write(s);
            writer.flush();
            writer.close();
        };
    }
    /**
     * 登入失败的处理器
     */
    public AuthenticationFailureHandler authenticationFailureHandler(){
        return (request, response, exception) -> {
            log.error("登入失败:{}",exception.getMessage());
            response.setContentType(HttpConstant.APPLICATION_JSON);
            response.setCharacterEncoding(HttpConstant.UTF_8);
            Result<String> result=new Result<>();
            result.setCode(BusinessEnum.UN_AUTHORIZATION.getCode());
            if (exception instanceof BadCredentialsException) {
                result.setMsg("用户名或者密码错误");
            } else if (exception instanceof UsernameNotFoundException) {
                result.setMsg("用户名错误");
            } else if (exception instanceof AccountExpiredException) {
                result.setMsg("账号异常，请联系管理员");
            } else if (exception instanceof InternalAuthenticationServiceException) {
                result.setMsg(exception.getMessage());
            } else {
                result.setMsg(BusinessEnum.UN_AUTHORIZATION.getDesc());
            }
            String s = JSON.toJSONString(result);
            PrintWriter writer = response.getWriter();
            writer.write(s);
            writer.flush();
            writer.close();
        };
    }
    /**
     * 退出成功的处理器
     */
    public LogoutSuccessHandler logoutSuccessHandler(){
        return (request, response, authentication) -> {
            response.setContentType(HttpConstant.APPLICATION_JSON);
            response.setCharacterEncoding(HttpConstant.UTF_8);
            String authorization = request.getHeader(AuthConstant.AUTHORIZATION);
            String token = authorization.replaceFirst(AuthConstant.BEARER, "");
            redisTemplate.delete(AuthConstant.LOGIN_TOKEN_PREFIX + token);
            Result<Object> result = Result.success(null);
            String s = JSON.toJSONString(result);
            PrintWriter writer = response.getWriter();
            writer.write(s);
            writer.flush();
            writer.close();
        };
    }

    /**
     * 密码匹配器
     */
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
}
